package main

import (
	"fmt"
	"gopkg.in/ini.v1"
	"gopkg.in/natefinch/lumberjack.v2"
	"log"
	"net/http"
	"os/exec"
	"strconv"
	"strings"
	"time"
)

func main() {

	// 创建一个新的 lumberjack.Logger 实例
	logger := &lumberjack.Logger{
		Filename:   "logs/app.log", // 日志文件路径
		MaxSize:    10,             // 每个日志文件的最大大小，单位是MB
		MaxBackups: 5,              // 保留的旧日志文件的最大数量
		MaxAge:     30,             // 保留的旧日志文件的最大天数
		Compress:   true,           // 是否启用压缩
	}

	// 设置输出
	log.SetOutput(logger)

	// 设置日志前缀
	log.SetPrefix("MyApp ")
	log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)

	// 加载INI文件
	cfg, err := ini.Load("config.ini")
	if err != nil {
		log.Println("Failed to load INI file:", err)
		return
	}

	// 获取某个section下的key的值
	ports := cfg.Section("").Key("port").String()
	// 获取某个section下的key的值
	cfgArrayKey := cfg.Section("").Keys()

	if len(ports) == 0 || len(cfgArrayKey) == 0 {
		log.Println("Failed read port  from config.ini file")
		return
	}

	portArray := strings.Split(ports, ",")

	if len(portArray) == 0 {
		log.Println("Failed read port  from config.ini file portArray is null")
		return
	}

	// 循环检查端口
	for {
		for i := 0; i < len(portArray); i++ {
			// 将字符串转换为整数类型
			port, err := strconv.Atoi(portArray[i])
			if err != nil {
				log.Println("转换失败：", err)
				return
			}
			go checkPort(port, true)
		}
		// 暂停1分钟
		time.Sleep(1 * time.Minute)
	}
}

func checkPort(port int, start bool) {
	// 发起HTTP请求
	client := http.Client{
		Timeout: 1 * time.Second,
	}
	resp, err := client.Get(fmt.Sprintf("http://localhost:%d", port))
	if err != nil {
		log.Printf("Error accessing port %d: %v\n", port, err)
	}

	// 检查响应状态码
	if (resp != nil && resp.StatusCode != http.StatusOK) || err != nil {
		log.Printf("Port %d is not responding with status code 200\n", port)
		// 结束对应的进程
		flag, err := killProcess(port)
		if err != nil {
			log.Printf("Error killing process for port %d: %v\n", port, err)
			return
		}
		if flag == 1 {
			log.Printf("Process for port %d has been terminated\n", port)
		} else {
			log.Printf("Process for port %d not exist, not killed\n", port)
		}
		if start {
			startProcess(port)
		}
	}
}

func startProcess(port int) {
	// 加载INI文件
	cfg, err := ini.Load("config.ini")
	if err != nil {
		log.Println("Failed to load INI file:", err)
		return
	}
	startCmd := cfg.Section("").Key(fmt.Sprintf("%d", port)).String()
	cmd := exec.Command("powershell.exe", startCmd)
	_, err = cmd.CombinedOutput()
	if err != nil {
		log.Printf("startProcess %d 失败,失败原因:%v", port, err)
	} else {
		time.Sleep(1 * time.Second)
		checkPort(port, false)
		time.Sleep(2 * time.Second)
		log.Printf("startProcess %d 成功:", port)
	}

}

func killProcess(port int) (flag int, err error) {
	cmd := exec.Command("powershell.exe", fmt.Sprintf("netstat -ano | findstr `\"0.0.0.0:%d`\"", port))
	output, err := cmd.CombinedOutput()
	if len(output) == 0 {
		return 0, nil
	}

	// 解析输出以获取PID
	// 假设PID在输出的第一个位置
	if len(output) > 0 {
		array := strings.Split(string(output), " ")
		if len(array) > 0 {
			pidStr := array[len(array)-1:]
			newStr := strings.Replace(pidStr[0], "\r", "", -1)
			newStr = strings.Replace(newStr, "\n", "", -1)
			pid, err := strconv.Atoi(newStr)
			if err != nil {
				return 1, err
			}
			// 结束进程
			killCmd := exec.Command("taskkill", "/F", "/PID", strconv.Itoa(pid))
			err = killCmd.Run()
			if err != nil {
				return 1, err
			}

			return 1, nil

		}
	}

	return 1, nil
}
